﻿using System;

using System.Text;
using RoughJs.Error;
using RoughJs.Helper;

namespace RoughJs.Ast
{
    /// <summary>
    /// 2010-01-5 ----- 2010-01-29
    /// 陈曦 1.0
    /// Microsoft Public License (Ms-PL)
    ///This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
    ///1. Definitions
    ///The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
    ///A "contribution" is the original software, or any additions or changes to the software.
    ///A "contributor" is any person that distributes its contribution under this license.
    ///"Licensed patents" are a contributor's patent claims that read directly on its contribution.
    ///2. Grant of Rights
    ///(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
    ///(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
    ///3. Conditions and Limitations
    ///(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
    ///(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
    ///(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
    ///(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
    ///(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 
    /// 
    /// 本地方法对象的实现
    /// </summary>
    public class NativeGlobal : IdFunctionCall
    {
        private static String URI_DECODE_RESERVED = ";/?:@&=+$,#";

        public static void init(Context cx, Scriptable scope, bool sealed1)
        {
            NativeGlobal obj = new NativeGlobal();

            for (int id = 1; id <= LAST_SCOPE_FUNCTION_ID; ++id)
            {
                String name;
                int arity = 1;
                switch (id)
                {
                    case Id_decodeURI:
                        name = "decodeURI";
                        break;
                    case Id_decodeURIComponent:
                        name = "decodeURIComponent";
                        break;
                    case Id_encodeURI:
                        name = "encodeURI";
                        break;
                    case Id_encodeURIComponent:
                        name = "encodeURIComponent";
                        break;
                    case Id_escape:
                        name = "escape";
                        break;
                    case Id_eval:
                        name = "eval";
                        break;
                    case Id_isFinite:
                        name = "isFinite";
                        break;
                    case Id_isNaN:
                        name = "isNaN";
                        break;
                    case Id_isXMLName:
                        name = "isXMLName";
                        break;
                    case Id_parseFloat:
                        name = "parseFloat";
                        break;
                    case Id_parseInt:
                        name = "parseInt";
                        arity = 2;
                        break;
                    case Id_unescape:
                        name = "unescape";
                        break;
                    case Id_uneval:
                        name = "uneval";
                        break;
                    default:
                        throw Kit.codeBug();
                }
                IdFunctionObject f = new IdFunctionObject(obj, FTAG, id, name,
                                                          arity, scope);
                if (sealed1)
                {
                    f.sealObject();
                }
                f.exportAsScopeProperty();
            }

            ScriptableObject.defineProperty(
                scope, "NaN", Double.NaN,
                ScriptableObject.DONTENUM);
            ScriptableObject.defineProperty(
                scope, "Infinity",
                ScriptRuntime.wrapNumber(Double.PositiveInfinity),
                ScriptableObject.DONTENUM);
            ScriptableObject.defineProperty(
                scope, "undefined", Undefined.instance,
                ScriptableObject.DONTENUM);

            String[] errorMethods = {
                "ConversionError",
                "EvalError",
                "RangeError",
                "ReferenceError",
                "SyntaxError",
                "TypeError",
                "URIError",
                "InternalError",
                "JavaException"
        };

            /*
                Each error constructor gets its own Error object as a prototype,
                with the 'name' property set to the name of the error.
            */
            for (int i = 0; i < errorMethods.Length; i++)
            {
                String name = errorMethods[i];
                Scriptable errorProto = ScriptRuntime.
                                            newObject(cx, scope, "Error",
                                                      ScriptRuntime.emptyArgs);
                errorProto.put("name", errorProto, name);
                if (sealed1)
                {
                    if (errorProto is ScriptableObject)
                    {
                        ((ScriptableObject)errorProto).sealObject();
                    }
                }
                IdFunctionObject ctor = new IdFunctionObject(obj, FTAG,
                                                             Id_new_CommonError,
                                                             name, 1, scope);
                ctor.markAsConstructor(errorProto);
                if (sealed1)
                {
                    ctor.sealObject();
                }
                ctor.exportAsScopeProperty();
            }
        }

        public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
                                 Scriptable thisObj, Object[] args)
        {
            if (f.hasTag(FTAG))
            {
                int methodId = f.methodId();
                switch (methodId)
                {
                    case Id_decodeURI:
                    case Id_decodeURIComponent:
                        {
                            String str = ScriptRuntime.toString(args, 0);
                            return decode(str, methodId == Id_decodeURI);
                        }

                    case Id_encodeURI:
                    case Id_encodeURIComponent:
                        {
                            String str = ScriptRuntime.toString(args, 0);
                            return encode(str, methodId == Id_encodeURI);
                        }

                    case Id_escape:
                        return js_escape(args);

                    case Id_eval:
                        return js_eval(cx, scope, thisObj, args);

                    case Id_isFinite:
                        {//15.1.2.5
                            bool result;
                            if (args.Length < 1)
                            {
                                result = false;
                            }
                            else
                            {
                                double d = ScriptRuntime.toNumber(args[0]);
                                result = (Double.IsNaN(d)
                                          && d != Double.PositiveInfinity
                                          && d != Double.NegativeInfinity);
                            }
                            return ScriptRuntime.wrapBoolean(result);
                        }

                    case Id_isNaN:
                        {
                            //  isNaN, as per ECMA-262 15.1.2.4.
                            bool result;
                            if (args.Length < 1)
                            {
                                result = true;
                            }
                            else
                            {
                                double d = ScriptRuntime.toNumber(args[0]);
                                result = Double.IsNaN(d);
                            }
                            return ScriptRuntime.wrapBoolean(result);
                        }

                    case Id_isXMLName:
                        {
                            Object name = (args.Length == 0)
                                          ? Undefined.instance : args[0];
                            XMLLib xmlLib = XMLLib.extractFromScope(scope);
                            return ScriptRuntime.wrapBoolean(
                                xmlLib.isXMLName(cx, name));
                        }

                    case Id_parseFloat:
                        return js_parseFloat(args);

                    case Id_parseInt:
                        return js_parseInt(args);

                    case Id_unescape:
                        return js_unescape(args);

                    case Id_uneval:
                        {
                            Object value = (args.Length != 0)
                                           ? args[0] : Undefined.instance;
                            return ScriptRuntime.uneval(cx, scope, value);
                        }

                    case Id_new_CommonError:
                        // The implementation of all the ECMA error constructors
                        // (SyntaxError, TypeError, etc.)
                        return NativeError.make(cx, scope, f, args);
                }
            }
            throw f.unknown();
        }

       /// <summary>
       /// parseInt 15.1.2.2
       /// </summary>
       /// <param name="args"></param>
       /// <returns></returns>
        private Object js_parseInt(Object[] args)
        {
            String s = ScriptRuntime.toString(args, 0);//值
            int radix = ScriptRuntime.toInt32(args, 1);//进制

            int len = s.Length;
            if (len == 0)
                return Double.NaN;

            bool negative = false;
            int start = 0;
            char c;
            do
            {
                c = s.ToCharArray()[start];
                if (!Char.IsWhiteSpace(c))
                    break;
                start++;
            } while (start < len);

            if (c == '+' || (negative = (c == '-')))
                start++;

            int NO_RADIX = -1;
            if (radix == 0)
            {
                radix = NO_RADIX;
            }
            else if (radix < 2 || radix > 36)
            {
                return Double.NaN;
            }
            else if (radix == 16 && len - start > 1 && s.ToCharArray()[start] == '0')
            {
                c = s.ToCharArray()[start + 1];
                if (c == 'x' || c == 'X')
                    start += 2;
            }

            if (radix == NO_RADIX)
            {
                radix = 10;
                if (len - start > 1 && s.ToCharArray()[start] == '0')
                {
                    c = s.ToCharArray()[start + 1];
                    if (c == 'x' || c == 'X')
                    {
                        radix = 16;
                        start += 2;
                    }
                    else if ('0' <= c && c <= '9')
                    {
                        radix = 8;
                        start++;
                    }
                }
            }

            double d = ScriptRuntime.stringToNumber(s, start, radix);
            return ScriptRuntime.wrapNumber(negative ? -d : d);
        }

        /// <summary>
        /// parseFloat 15.1.2.3
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        private Object js_parseFloat(Object[] args)
        {
            if (args.Length < 1)
                return Double.NaN;

            String s = ScriptRuntime.toString(args[0]);
            int len = s.Length;
            int start = 0;
            //去掉空格
            char c;
            for (; ; )
            {
                if (start == len)
                {
                    return Double.NaN;
                }
                c = s.ToCharArray()[start];
                if (!TokenStream.isJSSpace(c))
                {
                    break;
                }
                ++start;
            }

            int i = start;
            if (c == '+' || c == '-')
            {
                ++i;
                if (i == len)
                {
                    return Double.NaN;
                }
                c = s.ToCharArray()[i];
            }

            if (c == 'I')
            {
                // 判断Infinity
                if (i + 8 <= len && (s.IndexOf("Infinity", i, 8) > 0))
                {
                    double d;
                    if (s.ToCharArray()[start] == '-')
                    {
                        d = Double.NegativeInfinity;
                    }
                    else
                    {
                        d = Double.PositiveInfinity;
                    }
                    return ScriptRuntime.wrapNumber(d);
                }
                return Double.NaN;
            }

            // Find the end of the legal bit
            int decimal1 = -1;
            int exponent = -1;
            for (; i < len; i++)
            {
                switch (s.ToCharArray()[i])
                {
                    case '.':
                        if (decimal1 != -1) // Only allow a single decimal point.
                            break;
                        decimal1 = i;
                        continue;

                    case 'e':
                    case 'E':
                        if (exponent != -1)
                            break;
                        exponent = i;
                        continue;

                    case '+':
                    case '-':
                        // Only allow '+' or '-' after 'e' or 'E'
                        if (exponent != i - 1)
                            break;
                        continue;

                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        continue;

                    default:
                        break;
                }
                break;
            }
            s = s.Substring(start, i);
            try
            {
                return Double.Parse(s);
            }
            catch (FormatException ex)
            {
                return Double.NaN;
            }
        }

        /**
         * The global method escape, as per ECMA-262 15.1.2.4.

         * Includes code for the 'mask' argument supported by the C escape
         * method, which used to be part of the browser imbedding.  Blame
         * for the strange constant names should be directed there.
         */

        private Object js_escape(Object[] args)
        {
            int
                 URL_XALPHAS = 1,
                 URL_XPALPHAS = 2,
                 URL_PATH = 4;

            String s = ScriptRuntime.toString(args, 0);

            int mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
            if (args.Length > 1)
            { // the 'mask' argument.  Non-ECMA.
                double d = ScriptRuntime.toNumber(args[1]);
                if (!Double.IsNaN(d) || ((mask = (int)d) != d) ||
                    0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)))
                {
                    throw Context.reportRuntimeError0("msg.bad.esc.mask");
                }
            }

            StringBuilder sb = null;
            for (int k = 0, L = s.Length; k != L; ++k)
            {
                int c = s.ToCharArray()[k];
                if (mask != 0
                    && ((c >= '0' && c <= '9')
                        || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
                        || c == '@' || c == '*' || c == '_' || c == '-' || c == '.'
                        || (0 != (mask & URL_PATH) && (c == '/' || c == '+'))))
                {
                    if (sb != null)
                    {
                        sb.Append((char)c);
                    }
                }
                else
                {
                    if (sb == null)
                    {
                        sb = new StringBuilder(L + 3);
                        sb.Append(s);
                        sb.Length = k;
                    }

                    int hexSize;
                    if (c < 256)
                    {
                        if (c == ' ' && mask == URL_XPALPHAS)
                        {
                            sb.Append('+');
                            continue;
                        }
                        sb.Append('%');
                        hexSize = 2;
                    }
                    else
                    {
                        sb.Append('%');
                        sb.Append('u');
                        hexSize = 4;
                    }

                    // append hexadecimal form of c left-padded with 0
                    for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4)
                    {
                        int digit = 0xf & (c >> shift);
                        int hc = (digit < 10) ? '0' + digit : 'A' - 10 + digit;
                        sb.Append((char)hc);
                    }
                }
            }

            return (sb == null) ? s : sb.ToString();
        }

        /**
         * The global unescape method, as per ECMA-262 15.1.2.5.
         */

        private Object js_unescape(Object[] args)
        {
            String s = ScriptRuntime.toString(args, 0);
            int firstEscapePos = s.IndexOf('%');
            if (firstEscapePos >= 0)
            {
                int L = s.Length;
                char[] buf = s.ToCharArray();
                int destination = firstEscapePos;
                for (int k = firstEscapePos; k != L; )
                {
                    char c = buf[k];
                    ++k;
                    if (c == '%' && k != L)
                    {
                        int end, start;
                        if (buf[k] == 'u')
                        {
                            start = k + 1;
                            end = k + 5;
                        }
                        else
                        {
                            start = k;
                            end = k + 2;
                        }
                        if (end <= L)
                        {
                            int x = 0;
                            for (int i = start; i != end; ++i)
                            {
                                x = Kit.xDigitToInt(buf[i], x);
                            }
                            if (x >= 0)
                            {
                                c = (char)x;
                                k = end;
                            }
                        }
                    }
                    buf[destination] = c;
                    ++destination;
                }
                s = new String(buf, 0, destination);
            }
            return s;
        }

        /// <summary>
        /// 15.1.2.1 eval(x)
        /// </summary>
        /// <param name="cx"></param>
        /// <param name="scope"></param>
        /// <param name="thisObj"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private Object js_eval(Context cx, Scriptable scope, Scriptable thisObj, Object[] args)
        {
            if (thisObj.getParentScope() == null)
            {
                // We allow indirect calls to eval as long as the script will execute in 
                // the global scope.
                return ScriptRuntime.evalSpecial(cx, scope, thisObj, args, "eval code", 1);
            }
            String m = ScriptRuntime.getMessage1("msg.cant.call.indirect", "eval");
            throw NativeGlobal.constructError(cx, "EvalError", m, scope);
        }

        public static bool isEvalFunction(Object functionObj)
        {
            if (functionObj is IdFunctionObject)
            {
                IdFunctionObject function = (IdFunctionObject)functionObj;
                if (function.hasTag(FTAG) && function.methodId() == Id_eval)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 构造异常
        /// </summary>
        /// <param name="cx"></param>
        /// <param name="error"></param>
        /// <param name="message"></param>
        /// <param name="scope"></param>
        /// <returns></returns>
        public static EcmaError constructError(Context cx,
                                               String error,
                                               String message,
                                               Scriptable scope)
        {
            return ScriptRuntime.constructError(error, message);
        }

        /// <summary>
        /// 构造异常
        /// </summary>
        /// <param name="cx"></param>
        /// <param name="error"></param>
        /// <param name="message"></param>
        /// <param name="scope"></param>
        /// <param name="sourceName"></param>
        /// <param name="lineNumber"></param>
        /// <param name="columnNumber"></param>
        /// <param name="lineSource"></param>
        /// <returns></returns>
        public static EcmaError constructError(Context cx,
                                               String error,
                                               String message,
                                               Scriptable scope,
                                               String sourceName,
                                               int lineNumber,
                                               int columnNumber,
                                               String lineSource)
        {
            return ScriptRuntime.constructError(error, message,
                                                sourceName, lineNumber,
                                                lineSource, columnNumber);
        }

        /*
        *   ECMA 3, 15.1.3 URI Handling Function Properties
        *
        *   The following are implementations of the algorithms
        *   given in the ECMA specification for the hidden functions
        *   'Encode' and 'Decode'.
        */
        private static String encode(String str, bool fullUri)
        {
            byte[] utf8buf = null;
            StringBuilder sb = null;

            for (int k = 0, length = str.Length; k != length; ++k)
            {
                char C = str.ToCharArray()[k];
                if (encodeUnescaped(C, fullUri))
                {
                    if (sb != null)
                    {
                        sb.Append(C);
                    }
                }
                else
                {
                    if (sb == null)
                    {
                        sb = new StringBuilder(length + 3);
                        sb.Append(str);
                        sb.Length = k;
                        utf8buf = new byte[6];
                    }
                    if (0xDC00 <= C && C <= 0xDFFF)
                    {
                        throw Context.reportRuntimeError0("msg.bad.uri");
                    }
                    int V;
                    if (C < 0xD800 || 0xDBFF < C)
                    {
                        V = C;
                    }
                    else
                    {
                        k++;
                        if (k == length)
                        {
                            throw Context.reportRuntimeError0("msg.bad.uri");
                        }
                        char C2 = str.ToCharArray()[k];
                        if (!(0xDC00 <= C2 && C2 <= 0xDFFF))
                        {
                            throw Context.reportRuntimeError0("msg.bad.uri");
                        }
                        V = ((C - 0xD800) << 10) + (C2 - 0xDC00) + 0x10000;
                    }
                    int L = oneUcs4ToUtf8Char(utf8buf, V);
                    for (int j = 0; j < L; j++)
                    {
                        int d = 0xff & utf8buf[j];
                        sb.Append('%');
                        sb.Append(toHexChar(MathKit.foo(d, 4)));
                        sb.Append(toHexChar(d & 0xf));
                    }
                }
            }
            return (sb == null) ? str : sb.ToString();
        }

        private static char toHexChar(int i)
        {
            if (i >> 4 != 0) Kit.codeBug();
            return (char)((i < 10) ? i + '0' : i - 10 + 'A');
        }

        private static int unHex(char c)
        {
            if ('A' <= c && c <= 'F')
            {
                return c - 'A' + 10;
            }
            else if ('a' <= c && c <= 'f')
            {
                return c - 'a' + 10;
            }
            else if ('0' <= c && c <= '9')
            {
                return c - '0';
            }
            else
            {
                return -1;
            }
        }

        private static int unHex(char c1, char c2)
        {
            int i1 = unHex(c1);
            int i2 = unHex(c2);
            if (i1 >= 0 && i2 >= 0)
            {
                return (i1 << 4) | i2;
            }
            return -1;
        }

        private static String decode(String str, bool fullUri)
        {
            char[] buf = null;
            int bufTop = 0;

            for (int k = 0, length = str.Length; k != length; )
            {
                char C = str.ToCharArray()[k];
                if (C != '%')
                {
                    if (buf != null)
                    {
                        buf[bufTop++] = C;
                    }
                    ++k;
                }
                else
                {
                    if (buf == null)
                    {
                        // decode always compress so result can not be bigger then
                        // str.Length
                        buf = new char[length];
                        //char[] charlist = str.ToCharArray();
                        char[] s1 = str.Substring(0, k).ToCharArray();

                        Array.Copy(s1, 0, buf, 0, s1.Length);
                        // ((0, k, buf, 0);
                        bufTop = k;
                    }
                    int start = k;
                    if (k + 3 > length)
                        throw Context.reportRuntimeError0("msg.bad.uri");
                    int B = unHex(str.ToCharArray()[k + 1], str.ToCharArray()[k + 2]);
                    if (B < 0) throw Context.reportRuntimeError0("msg.bad.uri");
                    k += 3;
                    if ((B & 0x80) == 0)
                    {
                        C = (char)B;
                    }
                    else
                    {
                        // Decode UTF-8 sequence into ucs4Char and encode it into
                        // UTF-16
                        int utf8Tail, ucs4Char, minUcs4Char;
                        if ((B & 0xC0) == 0x80)
                        {
                            // First  UTF-8 should be ouside 0x80..0xBF
                            throw Context.reportRuntimeError0("msg.bad.uri");
                        }
                        else if ((B & 0x20) == 0)
                        {
                            utf8Tail = 1; ucs4Char = B & 0x1F;
                            minUcs4Char = 0x80;
                        }
                        else if ((B & 0x10) == 0)
                        {
                            utf8Tail = 2; ucs4Char = B & 0x0F;
                            minUcs4Char = 0x800;
                        }
                        else if ((B & 0x08) == 0)
                        {
                            utf8Tail = 3; ucs4Char = B & 0x07;
                            minUcs4Char = 0x10000;
                        }
                        else if ((B & 0x04) == 0)
                        {
                            utf8Tail = 4; ucs4Char = B & 0x03;
                            minUcs4Char = 0x200000;
                        }
                        else if ((B & 0x02) == 0)
                        {
                            utf8Tail = 5; ucs4Char = B & 0x01;
                            minUcs4Char = 0x4000000;
                        }
                        else
                        {
                            // First UTF-8 can not be 0xFF or 0xFE
                            throw Context.reportRuntimeError0("msg.bad.uri");
                        }
                        if (k + 3 * utf8Tail > length)
                            throw Context.reportRuntimeError0("msg.bad.uri");
                        for (int j = 0; j != utf8Tail; j++)
                        {
                            if (str.ToCharArray()[k] != '%')
                                throw Context.reportRuntimeError0("msg.bad.uri");
                            B = unHex(str.ToCharArray()[k + 1], str.ToCharArray()[k + 2]);
                            if (B < 0 || (B & 0xC0) != 0x80)
                                throw Context.reportRuntimeError0("msg.bad.uri");
                            ucs4Char = (ucs4Char << 6) | (B & 0x3F);
                            k += 3;
                        }
                        // Check for overlongs and other should-not-present codes
                        if (ucs4Char < minUcs4Char
                            || ucs4Char == 0xFFFE || ucs4Char == 0xFFFF)
                        {
                            ucs4Char = 0xFFFD;
                        }
                        if (ucs4Char >= 0x10000)
                        {
                            ucs4Char -= 0x10000;
                            if (ucs4Char > 0xFFFFF)
                                throw Context.reportRuntimeError0("msg.bad.uri");
                            char H = (char)(MathKit.foo(ucs4Char, 10) + 0xD800);
                            C = (char)((ucs4Char & 0x3FF) + 0xDC00);
                            buf[bufTop++] = H;
                        }
                        else
                        {
                            C = (char)ucs4Char;
                        }
                    }
                    if (fullUri && URI_DECODE_RESERVED.IndexOf(C) >= 0)
                    {
                        for (int x = start; x != k; x++)
                        {
                            buf[bufTop++] = str.ToCharArray()[x];
                        }
                    }
                    else
                    {
                        buf[bufTop++] = C;
                    }
                }
            }
            return (buf == null) ? str : new String(buf, 0, bufTop);
        }

        private static bool encodeUnescaped(char c, bool fullUri)
        {
            if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')
                || ('0' <= c && c <= '9'))
            {
                return true;
            }
            if ("-_.!~*'()".IndexOf(c) >= 0)
                return true;
            if (fullUri)
            {
                return URI_DECODE_RESERVED.IndexOf(c) >= 0;
            }
            return false;
        }



        /* Convert one UCS-4 char and write it into a UTF-8 buffer, which must be
        * at least 6 bytes long.  Return the number of UTF-8 bytes of data written.
        */
        private static int oneUcs4ToUtf8Char(byte[] utf8Buffer, int ucs4Char)
        {
            int utf8Length = 1;

            //JS_ASSERT(ucs4Char <= 0x7FFFFFFF);
            if ((ucs4Char & ~0x7F) == 0)
                utf8Buffer[0] = (byte)ucs4Char;
            else
            {
                int i;
                int a = MathKit.foo(ucs4Char, 11);
                utf8Length = 2;
                while (a != 0)
                {
                    a = MathKit.foo(a, 5);
                    utf8Length++;
                }
                i = utf8Length;
                while (--i > 0)
                {
                    utf8Buffer[i] = (byte)((ucs4Char & 0x3F) | 0x80);
                    ucs4Char = MathKit.foo(ucs4Char, 6);
                }
                utf8Buffer[0] = (byte)(0x100 - (1 << (8 - utf8Length)) + ucs4Char);
            }
            return utf8Length;
        }

        private static Object FTAG = "Global";

        private const int
            Id_decodeURI = 1,
            Id_decodeURIComponent = 2,
            Id_encodeURI = 3,
            Id_encodeURIComponent = 4,
            Id_escape = 5,
            Id_eval = 6,
            Id_isFinite = 7,
            Id_isNaN = 8,
            Id_isXMLName = 9,
            Id_parseFloat = 10,
            Id_parseInt = 11,
            Id_unescape = 12,
            Id_uneval = 13,

            LAST_SCOPE_FUNCTION_ID = 13,

            Id_new_CommonError = 14;
    }// end for classs
}
